El objetivo de esta prueba de evaluación continua es que el estudiante sea capaz de crear una narrativa de datos con técnicas de visualización, es decir, una visualización tipo storytelling. Hans Rossling, unos de los líderes de narrativa de datos, en uno de sus famosos videos comenta: “Tener los datos no es suficiente, hay que mostrarlos de forma que la gente los disfrute y los entienda”.
Hoy en día, Netflix es una de las plataformas más usadas para ver contenido online, es por eso, que vamos a investigar en que momento empezó realmente a ser más conocida, y acercándonos a la actualidad (2023), las poblaciones que la consumen más.
# Importamos las librerías necesarios
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.3 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.1 ✔ tidyr 1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks plotly::filter(), stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(scales)
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
library(lubridate)
library(visdat)
library(igraph)
##
## Attaching package: 'igraph'
##
## The following objects are masked from 'package:lubridate':
##
## %--%, union
##
## The following objects are masked from 'package:dplyr':
##
## as_data_frame, groups, union
##
## The following objects are masked from 'package:purrr':
##
## compose, simplify
##
## The following object is masked from 'package:tidyr':
##
## crossing
##
## The following object is masked from 'package:tibble':
##
## as_data_frame
##
## The following object is masked from 'package:plotly':
##
## groups
##
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
##
## The following object is masked from 'package:base':
##
## union
library(networkD3)
library(wordcloud)
## Loading required package: RColorBrewer
library(tm)
## Loading required package: NLP
##
## Attaching package: 'NLP'
##
## The following object is masked from 'package:ggplot2':
##
## annotate
library(slam)
El conjunto de datos netflix_titles.xlsx se encuentra disponible en la plataforma Kaggle: https://www.kaggle.com/datasets/shivamb/netflix-shows
Este conjunto de datos tabulares consiste en listados de las películas y programas de televisión disponibles en Netflix, junto con detalles como el reparto, los directores, las puntuaciones, el año de estreno, la duración, etc.
Las variables que presenta el conjunto de datos son:
show_id: identificador único para cada película o serie de Netflix.
type: identificador película o serie (Movie o TV Show).
title: título del contenido.
director: director del contenido.
cast: actores involucrados en el contenido.
country: país donde se produjo el contenido.
date_added: fecha en la que fue añadido el contenido en Netflix.
release_year: año actual del lanzamiento del contenido.
rating: rating televisivo del contenido.
duration: duración total en minutos o nº de temporadas.
listed_in: género.
description: resumen descriptivo del contenido.
# Importamos el conjunto de datos y vemos la estructura que tiene
library(readxl)
netflix_titles <- read_excel("~/Documents/AAESTUDIOS/UOC_Máster_Data_Science/4t_Semestre/Visualización_datos/PEC3/netflix_titles.xlsx",
skip = 1)
View(netflix_titles)
# Resumen de las variables del conjunto de datos
glimpse(netflix_titles)
## Rows: 8,807
## Columns: 12
## $ show_id <chr> "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s1…
## $ type <chr> "Movie", "TV Show", "TV Show", "TV Show", "TV Show", "TV …
## $ title <chr> "Dick Johnson Is Dead", "Blood & Water", "Ganglands", "Ja…
## $ director <chr> "Kirsten Johnson", NA, "Julien Leclercq", NA, NA, "Mike F…
## $ cast <chr> NA, "Ama Qamata, Khosi Ngema, Gail Mabalane, Thabang Mola…
## $ country <chr> "United States", "South Africa", NA, NA, "India", NA, NA,…
## $ date_added <chr> "September 25, 2021", "September 24, 2021", "September 24…
## $ release_year <dbl> 2020, 2021, 2021, 2021, 2021, 2021, 2021, 1993, 2021, 202…
## $ rating <chr> "PG-13", "TV-MA", "TV-MA", "TV-MA", "TV-MA", "TV-MA", "PG…
## $ duration <chr> "90 min", "2 Seasons", "1 Season", "1 Season", "2 Seasons…
## $ listed_in <chr> "Documentaries", "International TV Shows, TV Dramas, TV M…
## $ description <chr> "As her father nears the end of his life, filmmaker Kirst…
# Visualizamos los missing values y los limpiamos
vis_miss(netflix_titles, sort_miss = T, cluster = T)
mode <- function(x){
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
#Eliminamos los missings y demostramos que ya no hay
netflix_titles <- netflix_titles %>%
replace_na(list(country = mode(netflix_titles$country), rating = mode(netflix_titles$rating),
director = "Unknown", cast = "Unknown",
date_added = mode(netflix_titles$date_added)))
vis_miss(netflix_titles)
anyDuplicated(netflix_titles)
## [1] 0
# Obtenemos nuevas columnas a partir de country, cast y listed_in
# También se cambian las etiquetas de la clasificación a 4 categorías distintas
netflix_titles <- netflix_titles %>%
mutate(main_country = map(str_split(country, ", "), 1),
main_cast = map(str_split(cast, ", "), 1),
genre = map(str_split(listed_in, ", "), 1)) %>%
unnest(cols = c(main_country, main_cast, genre)) %>%
mutate(type = as.factor(type),
date_added = mdy(date_added),
year_added = year(date_added),
main_country = str_remove(main_country, ","),
target_age = factor(sapply(rating, switch,
'TV-PG' = 'Older Kids',
'TV-MA' = 'Adults',
'TV-Y7-FV' = 'Older Kids',
'TV-Y7' = 'Older Kids',
'TV-14' = 'Teens',
'R' = 'Adults',
'TV-Y' = 'Kids',
'NR' = 'Adults',
'PG-13' = 'Teens',
'TV-G' = 'Kids',
'PG' = 'Older Kids',
'G' = 'Kids',
'UR' = 'Adults',
'NC-17' = 'Adults'), level = c("Kids", "Older Kids", "Teens", "Adults"))
)
head(netflix_titles)
## # A tibble: 6 × 17
## show_id type title director cast country date_added release_year rating
## <chr> <fct> <chr> <chr> <chr> <chr> <date> <dbl> <chr>
## 1 s1 Movie Dick Jo… Kirsten… Unkn… United… 2021-09-25 2020 PG-13
## 2 s2 TV Show Blood &… Unknown Ama … South … 2021-09-24 2021 TV-MA
## 3 s3 TV Show Ganglan… Julien … Sami… United… 2021-09-24 2021 TV-MA
## 4 s4 TV Show Jailbir… Unknown Unkn… United… 2021-09-24 2021 TV-MA
## 5 s5 TV Show Kota Fa… Unknown Mayu… India 2021-09-24 2021 TV-MA
## 6 s6 TV Show Midnigh… Mike Fl… Kate… United… 2021-09-24 2021 TV-MA
## # ℹ 8 more variables: duration <chr>, listed_in <chr>, description <chr>,
## # main_country <chr>, main_cast <chr>, genre <chr>, year_added <dbl>,
## # target_age <fct>
# Creamos un corpus con los títulos
corpus <- Corpus(VectorSource(netflix_titles$title))
corpus
## <<SimpleCorpus>>
## Metadata: corpus specific: 1, document level (indexed): 0
## Content: documents: 8807
# Preprocesamos el texto
corpus <- tm_map(corpus, content_transformer(tolower)) # Convertir a minúsculas
## Warning in tm_map.SimpleCorpus(corpus, content_transformer(tolower)):
## transformation drops documents
corpus <- tm_map(corpus, removePunctuation) # Eliminar puntuación
## Warning in tm_map.SimpleCorpus(corpus, removePunctuation): transformation drops
## documents
corpus <- tm_map(corpus, removeNumbers) # Eliminar números
## Warning in tm_map.SimpleCorpus(corpus, removeNumbers): transformation drops
## documents
corpus <- tm_map(corpus, removeWords, stopwords("en")) # Eliminar palabras vacías en inglés
## Warning in tm_map.SimpleCorpus(corpus, removeWords, stopwords("en")):
## transformation drops documents
corpus <- tm_map(corpus, stripWhitespace) # Eliminar espacios en blanco adicionales
## Warning in tm_map.SimpleCorpus(corpus, stripWhitespace): transformation drops
## documents
# Generamos una matriz con los términos más usados de los titulos
tdm <- TermDocumentMatrix(corpus)
m <- as.matrix(tdm)
word_freqs <- sort(rowSums(m), decreasing = TRUE)
library(RColorBrewer)
# Definir colores similares a Netflix
netflix_colors <- c("#221F1F", "#B20710","#444444","#94040E", "#666666","#FF0000", "#888888", "#AAAAAA","#E50914")
# Generar la nube de palabras con los colores ajustados
wordcloud(words = names(word_freqs), freq = word_freqs, min.freq = 5,
max.words = 100, random.order = FALSE, colors = netflix_colors)
La siguiente visualización muestra el crecimiento de los contenidos de Netflix según los años, en este caso des de 2008 hasta 2021. En el gráfico, se observa el cambio de recuentos de forma acumulativa por cada contenido, ya sea película (Movie) o serie de televisión (TV SHow).
#Trabajamos en Flourish
library(dplyr)
# Agrupar por año y tipo, sumarizar los conteos acumulados
accumulated_data <- netflix_titles %>%
group_by(year_added, type) %>%
summarise(total_count = n()) %>%
arrange(year_added) %>%
group_by(type) %>%
mutate(accumulated_count = cumsum(total_count))
## `summarise()` has grouped output by 'year_added'. You can override using the
## `.groups` argument.
# Mostrar el nuevo conjunto de datos con los valores acumulados
print(accumulated_data)
## # A tibble: 24 × 4
## # Groups: type [2]
## year_added type total_count accumulated_count
## <dbl> <fct> <int> <int>
## 1 2008 Movie 1 1
## 2 2008 TV Show 1 1
## 3 2009 Movie 2 3
## 4 2010 Movie 1 4
## 5 2011 Movie 13 17
## 6 2012 Movie 3 20
## 7 2013 Movie 6 26
## 8 2013 TV Show 5 6
## 9 2014 Movie 19 45
## 10 2014 TV Show 5 11
## # ℹ 14 more rows
# Función para mostrar de manera acumulada e interactiva
accumulate_by <- function(dat, var) {
var <- lazyeval::f_eval(var, dat)
lvls <- plotly:::getLevels(var)
dats <- lapply(seq_along(lvls), function(x) {
cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
})
dplyr::bind_rows(dats)
}
# Transformaciones para acumular los contenidos de cada año
mtv_growth <- netflix_titles %>%
group_by(year_added, type) %>%
summarise(movie_count = n()) %>%
ungroup() %>%
mutate(cumulative_count = ave(movie_count, type, FUN = cumsum)) %>%
accumulate_by(~year_added) %>%
ggplot(aes(x = year_added, y = cumulative_count, color = type, frame = frame)) +
geom_line(size = 1.5, alpha = 0.8)+
geom_point(size = 3.5, shape = 21, fill = "white", aes(text = paste0("Year: ", year_added, "<br>",
"Content Count: ", cumulative_count))) +
scale_color_manual(values = c("firebrick", "grey16")) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Growth Numbers of Movies and TV Shows by Year",
x = "",
y = "Number of Movies/TV Shows",
color = "",
) +
theme_minimal() +
theme(title = element_text(face = "bold"))
## `summarise()` has grouped output by 'year_added'. You can override using the
## `.groups` argument.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in geom_point(size = 3.5, shape = 21, fill = "white", aes(text =
## paste0("Year: ", : Ignoring unknown aesthetics: text
# Gráfico interactivo con la animación
ggcont<-ggplotly(mtv_growth, tooltip = c("text", "frame")) %>%
config(displayModeBar = F)
# Guardar el gráfico interactivo como un archivo HTML
htmlwidgets::saveWidget(as_widget(ggcont), "/Users/luciablanc/Documents/AAESTUDIOS/UOC_Máster_Data_Science/4t_Semestre/Visualización_datos/PEC3/V2s3.html")
En esta visualización se muestran los contenidos de la plataforma de Netflix des de 2008 hasta 2021, lo que nos permite ver un aumento bastante notório a partir del año 2015 hasta la actualidad, sobretodo en contenido de películas.
Usamos el mapa cloropleth para diferenciar la distribución de la cantidad de contenidos de la plataforma de Netflix por país.
# Importamos los datos del mapa ggplot2
library(maps)
##
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
##
## map
mapdata <- map_data("world")
# Cambiamos las etiquetas de varíos países
netflix_for_map <- netflix_titles %>%
mutate(main_country = str_replace_all(main_country,
c("United States" = "USA",
"United Kingdom" = "UK",
"Hong Kong" = "China",
"Soviet Union" = "Russia",
"West Germany" = "Germany")))
# Contabilizamos y acumulamos el nº de contenidos por cada país
count_country <- netflix_for_map %>%
group_by(main_country) %>%
summarise(content_count = n()) %>%
ungroup() %>%
arrange(desc(content_count))
# Unimos los datos del mapa y los datos utilitzados para que coincidan
map_join <- mapdata %>%
left_join(. , count_country, by = c("region"="main_country")) %>%
mutate(content_count = replace_na(content_count, 0))
# Generamos el gráfico con ggplot() y plotly para hacer el gráfico interactivo
temp <- ggplot() +
geom_polygon(data = map_join,
aes(fill = content_count, x = long, y = lat,
group = group,
text = paste0(region, "<br>",
"Netflix Contents: ", content_count)),
size = 0, alpha = .9, color = "black"
) +
labs(title = "Distribution of Netflix Contents by Country") +
theme_void() +
scale_fill_gradient(name = "Content Count",
trans = "pseudo_log",
breaks = c(0, 7, 56, 403, 3000),
labels = c(0, 7, 56, 403, 3000),
low = "#666666",
high = "#B20710",) +
theme(panel.grid.major = element_blank(),
axis.line = element_blank(),
plot.title = element_text(face = "bold"))
## Warning in geom_polygon(data = map_join, aes(fill = content_count, x = long, :
## Ignoring unknown aesthetics: text
ggmap<-ggplotly(temp, tooltip = "text") %>%
config(displayModeBar = F) %>%
layout(legend = list(x = .1, y = .9))
ggmap
# Guardar el gráfico interactivo como un archivo HTML
htmlwidgets::saveWidget(as_widget(ggmap), "/Users/luciablanc/Documents/AAESTUDIOS/UOC_Máster_Data_Science/4t_Semestre/Visualización_datos/PEC3/Vis3.html")
En el gráfico generado se puede observar como Estados Unidos, con un total de 4.042, es el país que más contenidos produce en la plataforma de Netflix, hecho que tiene sentido, ya que, la plataforma se creó en este país.
También podemos observar, como India es el segundo país con más contenidos en la plataforma, distribuyendo un total de 1008 contenidos distintos.
En conclusión, los datos explotados de la plataforma de Netflix pueden ser explorados en mayor profundidad, ya que, contienen mucha información. En este ejercicio se ha explorado el crecimiento de los contenidos a lo largo de los años y la distribución de los contenidos según los países de todo el mundo.
Hemos podido comprobar como los contenidos de Netflix aumentaron significativamente a partir del año 2015, y que actualmente (o según los datos, hasta 2021) siguen creciendo de manera lineal. Por otro lado, también hemos podido reforzar que estos contenidos derivan principalmente de Estados Unidos y India, ya que contienen un elevado número de contenidos en comparación con el resto de paises.